package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.constants.RedisConstants;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.domain.query.PointsBoardQuery;
import com.tianji.learning.domain.vo.PointsBoardItemVO;
import com.tianji.learning.domain.vo.PointsBoardVO;
import com.tianji.learning.mapper.PointsBoardMapper;
import com.tianji.learning.service.IPointsBoardService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.utils.TableInfoContext;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.tianji.learning.domain.constants.LearningConstants.POINTS_BOARD_TABLE_PREFIX;

/**
 * <p>
 * 学霸天梯榜 服务实现类
 * </p>
 *
 * @author author
 * @since 2024-11-18
 */
@Service
@RequiredArgsConstructor
public class PointsBoardServiceImpl extends ServiceImpl<PointsBoardMapper, PointsBoard> implements IPointsBoardService {
        private final StringRedisTemplate stringRedisTemplate;
        private final UserClient userClient;
    /**
     * 分页查询积分排行榜
     *
     * @param query
     * @return
     */
    @Override
    public PointsBoardVO queryPointsBoardBySeason(PointsBoardQuery query) {
        //1.判断是否是当前赛季
        Long season = query.getSeason();
        boolean b = ObjectUtils.isEmpty(season) || season == 0;

        //2.获取redis的key
        LocalDateTime now = LocalDateTime.now();
        String key = RedisConstants.POINTS_BOARD_KEY_PREFIX + now.format(DateUtils.POINTS_BOARD_SUFFIX_FORMATTER);

        //3.查询我的排名和积分
        PointsBoard myBoard = b ?
                queryMyCurrentBoard(key) : //查询当前赛季排行
                queryMyHistoryBoard(season); //查询历史赛季排行

        //4.查询榜单
        List<PointsBoard> list = b ?
                queryCurrentBoardList(key, query.getPageNo(), query.getPageSize()) ://查询当前赛季榜单
                queryHistoryBoardList(query);   //查询历史赛季榜单

        //5.封装vo
        PointsBoardVO vo = new PointsBoardVO();
        //5.1填充我的信息
        if(myBoard != null){
            vo.setRank(myBoard.getRank());
            vo.setPoints(myBoard.getPoints());
        }
        if (CollUtils.isEmpty(list)){
            return vo;
        }

        //5.2查询用户信息
        Set<Long> userIds =  list.stream().map(PointsBoard::getUserId).collect(Collectors.toSet());
         List<UserDTO> users = userClient.queryUserByIds(userIds);
         //封装成map
        Map<Long, String> userMap = new HashMap<>(userIds.size());
        if (ObjectUtils.isNotEmpty(users)){
            userMap = users.stream().collect(Collectors.toMap(UserDTO::getId, UserDTO::getName));
        }

        //5.3填充voList
         List<PointsBoardItemVO> items = new ArrayList<>(list.size());
        for (PointsBoard p : list) {
            PointsBoardItemVO pointsBoardItemVO = new PointsBoardItemVO();
            pointsBoardItemVO.setRank(p.getRank());
            pointsBoardItemVO.setPoints(p.getPoints());
            pointsBoardItemVO.setName(userMap.get(p.getUserId()));
            items.add(pointsBoardItemVO);
        }
        vo.setBoardList(items);
        return vo;
    }

    /**
     * 创建历史表
     * @param season
     */
    @Override
    public void createPointsBoardTableBySeason(Integer season) {
        getBaseMapper().createPointsBoardTable(POINTS_BOARD_TABLE_PREFIX + season);
    }

    /**
     * 查询历史赛季排行榜
     * @param query
     * @return
     */
    private List<PointsBoard> queryHistoryBoardList(PointsBoardQuery query) {
        //1.拼接表名
        String tableName = POINTS_BOARD_TABLE_PREFIX + query.getSeason();

        //2.设置到ThreadLocal中
        TableInfoContext.setInfo(tableName);

        //3.查询
        Page<PointsBoard> page = this.lambdaQuery().page(query.toMpPage("id", true));
        return page.getRecords();
    }

    @Override
    public List<PointsBoard> queryCurrentBoardList(String key, Integer pageNo, Integer pageSize) {
        //1.计算分页
        int from = (pageNo -1)*pageSize;
        //2.查询
        Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
                .reverseRangeWithScores(key, from, from + pageSize - 1);
        if(CollUtils.isEmpty(typedTuples)){
            return CollUtils.emptyList();
        }
        //3.封装
        int rank = from + 1;
        ArrayList<PointsBoard> list = new ArrayList<>(typedTuples.size());
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            String userId = typedTuple.getValue();
            Double points = typedTuple.getScore();
            if(userId == null || points == null){
                continue;
            }
            PointsBoard p = new PointsBoard();
            p.setUserId(Long.valueOf(userId));
            p.setPoints(points.intValue());
            p.setRank(rank++);
            list.add(p);
        }
        return list;
    }

    /**
     *查询我的历史赛季积分和排名
     * @param season
     * @return
     */
    private PointsBoard queryMyHistoryBoard(Long season) {
        //1.拼接数据库表名
        String tableName = POINTS_BOARD_TABLE_PREFIX + season;
        //2.获取用户id
        Long userId = UserContext.getUser();
        //3.设置表明到ThreadLocal
        TableInfoContext.setInfo(tableName);
        //4.查询
        return this.lambdaQuery().eq(PointsBoard::getUserId, userId).one();
    }

    private PointsBoard queryMyCurrentBoard(String key) {
        //1.绑定key
        BoundZSetOperations<String, String> ops = stringRedisTemplate.boundZSetOps(key);
        //2.获取当前用户信息
        String user = UserContext.getUser().toString();
        //3.查询积分
        Double points = ops.score(user);
        //4.查询排名
        Long rank = ops.reverseRank(user);
        //5.封装返回
        PointsBoard p = new PointsBoard();
        p.setPoints(points == null? 0 : points.intValue());
        p.setRank(rank == null ? 0 : rank.intValue() + 1);
        return p;
    }
}
